package com.example.lemoncommon.auth;

import com.alibaba.fastjson.JSON;
import com.example.lemoncommon.annotation.GetPermitNoAuth;
import com.example.lemoncommon.entity.UserTokenEntity;
import com.example.lemoncommon.enums.ErrorEnum;
import com.example.lemoncommon.exception.ServiceException;
import com.example.lemoncommon.util.RedisCache;
import com.example.lemoncommon.util.UserTokenUtils;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 *
 */
@Aspect
@Component
@Order(0)
@Slf4j
public class TokenAop {

    @Autowired
    RedisCache redisTemplate;

    @Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
    public void controllerMethodPointcut() {
    }

    @Before("controllerMethodPointcut()")
    public void beforeInterceptor(JoinPoint joinPoint) throws Exception {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        request.setCharacterEncoding("UTF-8");
        String requestPath = request.getRequestURI();
        log.info("request uri: " + requestPath);
        GetPermitNoAuth permitNoAuth = containsNoAuth(joinPoint);
        if (permitNoAuth != null && permitNoAuth.requestMethod().equals(request.getMethod())) {
            log.info("请求方式{},{}方法跳过鉴权访问，参数{}", permitNoAuth.requestMethod(), joinPoint.toShortString(), joinPoint.getArgs());
            int limit = permitNoAuth.limit();
            int sec = permitNoAuth.sec();
            String key = request.getRequestURI();
            key += "(" + request.getHeader("user-agent") + ")";
            Integer maxLimit = redisTemplate.getCacheObject(key);
            if (maxLimit == null) {
                redisTemplate.setCacheObject(key, 1, sec, TimeUnit.SECONDS);
            } else if (maxLimit < limit) {
                redisTemplate.setCacheObject(key, maxLimit + 1, sec, TimeUnit.SECONDS);
            } else {
                throw new ServiceException(ErrorEnum.TOO_FAST_REQUEST.getErrCode(), ErrorEnum.TOO_FAST_REQUEST.getErrMsg());
            }
            return;
        }

        UserTokenEntity userTokenEntity = null;
        String token = request.getHeader(TokenConstants.OLD_WEB_USER_TOKEN);

        List<String> tokenExcludes = Lists.newArrayList("/noAuth");
        Set<String> result = new HashSet<>(tokenExcludes);
        for (String str : result) {
            if (requestPath.contains(str)) {
                return;
            }
        }

        if (requestPath.endsWith("Excel")) {
            return;
        }
        try {
            userTokenEntity = UserTokenUtils.parseToken(token);
        } catch (Exception e) {
            throw new ServiceException(ErrorEnum.INVALID_TOKEN_ERROR.getErrCode(), ErrorEnum.INVALID_TOKEN_ERROR.getErrMsg());
        }

        log.info("UserTokenEntity is {}" + userTokenEntity.toString());
    }

    @AfterReturning(returning = "ret", pointcut = "controllerMethodPointcut()")
    public void doAfterReturning(JoinPoint point, Object ret) throws Throwable {
        log.info("service return toJsonString:" + JSON.toJSONString(ret));

//        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        HttpServletRequest request = attributes.getRequest();
        // 处理完请求，返回内容
        log.info("ret" + ret);
        log.info("ret toJsonString:" + JSON.toJSONString(ret));
    }


    /*
     * 是否包含 特定注解
     * @date 2022/11/8 17:33
     * @param joinPoint
     * @return boolean
     */
    private GetPermitNoAuth containsNoAuth(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        if (method.isAnnotationPresent(GetPermitNoAuth.class)) {
            return method.getAnnotation(GetPermitNoAuth.class);
        }
//        GetPermitNoAuth[] declaredAnnotationsByType = method.getDeclaredAnnotationsByType(GetPermitNoAuth.class);
//        if (declaredAnnotationsByType.length > 0) {
//            return declaredAnnotationsByType[0];
//        }
        return null;
    }
}
